/*
 * Copyright (c) 2009. Silenus Consultoria, S.L.
 */
package es.silenus.detecta.controller;

import es.silenus.detecta.model.User;

import es.silenus.detecta.olap.PivotSettings;

import mondrian.olap.CacheControl;
import mondrian.olap.Connection;
import mondrian.olap.Cube;

import mondrian.rolap.RolapSchema;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;


/**
 * Pivot controller.
 *
 * @author <a href="mailto:malonso@silenus-consultoria.es">Mariano Alonso</a>
 *
 * @since 14-abr-2009 19:04:46
 */
public class PivotController extends BaseController {
	/**
	 * Logger for this class.
	 */
	private static final Log LOG = LogFactory.getLog(PivotController.class);

	/**
	 * Pivot id parameter.
	 */
	public static final String PIVOT_ID = "pivotId";

	/**
	 * The request uri parameter.
	 */
	public static final String REQUEST_URI = "requestURI";

	/**
	 * The settings.
	 */
	public static final String SETTINGS = "settings";

	/**
	 * The messages.
	 */
	public static final String MESSAGES = "messageSource";

	/**
	 * The pivot url.
	 */
	private String pivotUrl;

	/**
	 * The query parameter.
	 */
	private String queryParameter;

	/**
	 * Flag to use the query hash code to build pivot identifier.
	 */
	private boolean useQueryHash;

	/**
	 * The settings.
	 */
	private Map<String, PivotSettings> settings;

/**
   * Constructor.
   */
	public PivotController() {
		useQueryHash = false;
	}

	/**
	 * Sets the pivot url.
	 *
	 * @param pivotUrl the pivot url.
	 */
	public void setPivotUrl(final String pivotUrl) {
		this.pivotUrl = pivotUrl;
	}

	/**
	 * Sets the query parameter.
	 *
	 * @param queryParameter the query parameter.
	 */
	public void setQueryParameter(final String queryParameter) {
		this.queryParameter = queryParameter;
	}

	/**
	 * Sets the use query in hash flag.
	 *
	 * @param useQueryHash the use query in hash flag.
	 */
	public void setUseQueryHash(boolean useQueryHash) {
		this.useQueryHash = useQueryHash;
	}

	/**
	 * Sets the settings.
	 *
	 * @param settings the settings.
	 */
	public void setSettings(final Map<String, PivotSettings> settings) {
		this.settings = settings;
	}

	/**
	 * Retrieves the base settings.
	 *
	 * @param request the request.
	 *
	 * @return the settings.
	 */
	protected PivotSettings getBaseSettings(final HttpServletRequest request) {
		String path = request.getRequestURI();
		String contextPath = request.getContextPath();
		final String subPath = path.substring(contextPath.length());

		return settings.get(subPath);
	}

	/**
	 * Executes a pivot request.
	 *
	 * @param request the request.
	 * @param response the response.
	 */
	public void execute(final HttpServletRequest request, final HttpServletResponse response) {
		ServletContext servletContext = getServletContext();

		HttpSession session = request.getSession(false);

		if(session != null) {
			String userName = request.getRemoteUser();
			String theQuery = null;

			if(queryParameter != null) {
				theQuery = request.getParameter(queryParameter);
			}

			int pivotHash = userName.hashCode() + (31 * request.getRequestURI().hashCode());

			if((theQuery != null) && useQueryHash) {
				pivotHash += (31 * theQuery.hashCode());
			}

			String pivotId = String.valueOf(pivotHash);

			StringBuffer requestParameters = new StringBuffer();

			PivotSettings theSettings = (PivotSettings)session.getAttribute(pivotId);

			if(theSettings == null) {
				theSettings = new PivotSettings(getBaseSettings(request));
				requestParameters.append("?pivotId=").append(pivotId);
			} else {
				requestParameters.append('?');

				Enumeration e = request.getParameterNames();
				String param;

				while(e.hasMoreElements()) {
					param = (String)e.nextElement();
					requestParameters.append(param).append('=').append(request.getParameter(param));

					if(e.hasMoreElements()) {
						requestParameters.append('&');
					}
				}
			}

			if(theQuery != null) {
				theSettings.setQuery(theQuery);
			}

			// Log the request
			if(LOG.isDebugEnabled()) {
				StringBuilder buffer = new StringBuilder();

				buffer.append("Executing JPivot MDX: ").append(theSettings.getQuery());
				buffer.append(" for pivotId: ").append(pivotId);

				LOG.debug(buffer.toString());
			}

			request.setAttribute(PIVOT_ID, pivotId);
			request.setAttribute(REQUEST_URI, request.getRequestURI());
			request.setAttribute(MESSAGES, messageSource);

			session.setAttribute(pivotId, theSettings);


			RequestDispatcher dispatcher = servletContext.getRequestDispatcher(pivotUrl + requestParameters.toString());

			try {
				dispatcher.forward(request, response);
			} catch(Throwable e) {
				StringBuilder buffer = new StringBuilder();

				buffer.append("Error forwarding to pivot view");

				LOG.error(buffer.toString(), e);

				// TODO: fail with error
			}
		}
	}

	/**
	 * Flushes a mondrian schema.
	 *
	 * @param request the request.
	 * @param response the response.
	 *
	 * @return the view name.
	 */
	public String flushMondrian(final HttpServletRequest request, final HttpServletResponse response) {
		User user = getUser(request);

		boolean flushAll = (request.getParameter("all") != null);
		boolean flushSchemaCache = (request.getParameter("schema") != null);

		PivotSettings settings = getBaseSettings(request);

		Connection connection = null;

		try {
			if(flushAll) {
				Iterator<RolapSchema> schemaIt = RolapSchema.getRolapSchemas();
				CacheControl cacheControl = null;
				RolapSchema schema = null;

				while(schemaIt.hasNext()) {
					schema = schemaIt.next();
					cacheControl = schema.getInternalConnection().getCacheControl(null);

					for(Cube cube : schema.getCubes()) {
						cacheControl.flush(cacheControl.createMeasuresRegion(cube));
					}
				}

				if(flushSchemaCache && (cacheControl != null)) {
					cacheControl.flushSchemaCache();
				}
			} else {
				// Build connection
				connection = settings.getConnection(user.getLocale(), getServletContext());

				// Retrieve cache control
				CacheControl cacheControl = connection.getCacheControl(null);

				// Flush every cube
				for(Cube cube : connection.getSchema().getCubes()) {
					cacheControl.flush(cacheControl.createMeasuresRegion(cube));
				}

				// Flush schema ?
				if(flushSchemaCache) {
					cacheControl.flushSchemaCache();
				}
			}
		} finally {
			if(connection != null) {
				connection.close();
			}
		}

		return "secure/organization/flush";
	}
}
